/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
#include "pid.h"
#include <math.h>
#include "rtthread.h"

#define SIGMA 0.000001f
#define M_PI_FLOAT 3.14159265358979323846f
#define _ISFINITE(x) isfinite(x)

void pid_init(PID_t *pid, pid_mode_t mode, float dt_min)
{
    pid->mode = mode;
    pid->dt_min = dt_min;
    pid->kp = 0.0f;
    pid->ki = 0.0f;
    pid->kd = 0.0f;
    pid->integral = 0.0f;
    pid->integral_limit = 0.0f;
    pid->output_limit = 0.0f;
    pid->error_previous = 0.0f;
    pid->last_output = 0.0f;
}

int pid_set_parameters(PID_t *pid, float kp, float ki, float kd, float integral_limit, float output_limit)
{
    int ret = 0;

    if (_ISFINITE(kp))
    {
        pid->kp = kp;
    }
    else
    {
        ret = 1;
    }

    if (_ISFINITE(ki))
    {
        pid->ki = ki;
    }
    else
    {
        ret = 1;
    }

    if (_ISFINITE(kd))
    {
        pid->kd = kd;
    }
    else
    {
        ret = 1;
    }

    if (_ISFINITE(integral_limit))
    {
        pid->integral_limit = integral_limit;
    }
    else
    {
        ret = 1;
    }

    if (_ISFINITE(output_limit))
    {
        pid->output_limit = output_limit;
    }
    else
    {
        ret = 1;
    }

    return ret;
}

float pid_calculate(PID_t *pid, float sp, float val, float val_dot, float dt)
{
    if (!_ISFINITE(sp) || !_ISFINITE(val) || !_ISFINITE(val_dot) || !_ISFINITE(dt))
    {
        return pid->last_output;
    }

    float i, d;

    /* current error value */
    float error = sp - val;

    /* current error derivative */
    if (pid->mode == PID_MODE_DERIVATIV_CALC)
    {
        d = (error - pid->error_previous) / fmaxf(dt, pid->dt_min);
        pid->error_previous = error;
    }
    else if (pid->mode == PID_MODE_DERIVATIV_CALC_NO_SP)
    {
        d = (-val - pid->error_previous) / fmaxf(dt, pid->dt_min);
        pid->error_previous = -val;
    }
    else if (pid->mode == PID_MODE_DERIVATIV_SET)
    {
        d = -val_dot;
    }
    else
    {
        d = 0.0f;
    }

    if (!_ISFINITE(d))
    {
        d = 0.0f;
    }

    /* calculate PD output */
    float output = (error * pid->kp) + (d * pid->kd);

    if (pid->ki > SIGMA)
    {
        // Calculate the error integral and check for saturation
        i = pid->integral + (error * pid->ki * dt);

        /* check for saturation */
        if (_ISFINITE(i))
        {
            if ((pid->output_limit < SIGMA || (fabsf(output + i) <= pid->output_limit)) &&
                fabsf(i) <= pid->integral_limit)
            {
                /* not saturated, use new integral value */
                pid->integral = i;
            }
        }

        /* add I component to output */
        output += pid->integral;
    }

    /* limit output */
    if (_ISFINITE(output))
    {
        if (pid->output_limit > SIGMA)
        {
            if (output > pid->output_limit)
            {
                output = pid->output_limit;
            }
            else if (output < -pid->output_limit)
            {
                output = -pid->output_limit;
            }
        }

        pid->last_output = output;
    }

    return pid->last_output;
}
float pid_calculate_lpf(PID_t *pid, float sp, float val, float dt, float f_cut)
{
    if (!_ISFINITE(sp) || !_ISFINITE(val) || !_ISFINITE(dt))
    {
        return pid->last_output;
    }

    float i, d;

    float RC = 1.f / (2.f * M_PI_FLOAT * f_cut);
    float k = dt / (RC + dt);

    /* current error value */
    float error = sp - val;

    d = (error - pid->error_previous) * pid->kd / fmaxf(dt, pid->dt_min);
    pid->error_previous = error;

    pid->d_lpf = pid->d_lpf + (d - pid->d_lpf) * k;

    if (!_ISFINITE(pid->d_lpf))
    {
        pid->d_lpf = 0.0f;
    }

    /* calculate PD output */
    float output = (error * pid->kp) + (pid->d_lpf);

    if (pid->ki > SIGMA)
    {
        // Calculate the error integral and check for saturation
        i = pid->integral + (error * pid->ki * dt);

        /* check for saturation */
        if (_ISFINITE(i))
        {
            if ((pid->output_limit < SIGMA || (fabsf(output + i) <= pid->output_limit)) &&
                fabsf(i) <= pid->integral_limit)
            {
                /* not saturated, use new integral value */
                pid->integral = i;
            }
        }

        /* add I component to output */
        output += pid->integral;
    }

    /* limit output */
    if (_ISFINITE(output))
    {
        if (pid->output_limit > SIGMA)
        {
            if (output > pid->output_limit)
            {
                output = pid->output_limit;
            }
            else if (output < -pid->output_limit)
            {
                output = -pid->output_limit;
            }
        }

        pid->last_output = output;
    }

    return pid->last_output;
}

void pid_reset_integral(PID_t *pid)
{
    pid->integral = 0.0f;
}
